package com.study.kt._04project.encrypt

import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.FileReader
import java.io.FileWriter
import java.security.Key
import java.util.*
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.DESKeySpec
import javax.crypto.spec.SecretKeySpec

/** 常见对称加密算法
 *      1.凯撒加密算法：字符串偏移太简单，安全度太低
 *      2.DES：数据加密标准
 *      3.AES：高级数据加密标准，优先选择
 *
 *   需要秘钥，可以自己指定一个密钥加密、解密，底层使用操作二进制(0、1)，即操作字符ascii的二进制
 *
 *   DES与AES密钥的字节长度：
 *     1.DES的密钥要求64位 8个字节，而实际上只使用了前56位，最有8位用于校验
 *     2.AES要求128位，16个字节
 *
 *
 *     DES/AES是算法名称，除此之外还有工作模式/填充模式
 *     AES/CBC/NoPadding(128)
 *     AES/CBC/PKCS5Padding(128)
 *     DES/ECB/NoPadding(56)
 *     DES/ECB/PKCS5Padding(56)  。。。。等
 *
 *     工作模式
 *        EBC：电子密码本，每块独立加密，分块并行处理速度快，但是同样的原文得要相同的秘闻，容易被攻击
 *        CBC：密码分组连接，每块加密依赖前一块密文，同样的原文得到不同的密文，原文微小改动影响后面全部密文，但是加密需要串行处理误差传递
 *     填充模式：Padding
 *         对需要按块处理的数据，当数据长度不符合块处理需求时，按照一定方法填充满块长的一种规则
 */
fun main() {
    //testDES()
    //testAES()
    cryptHttpApi()
}

//----------------------------应用实战：优先选择AES-----------------------------
//在读取到本地联系人之后可以加密后缓存到自己应用的数据库中
fun cryptHttpApi() {
    val json = "{\"res\":\"slx\"}"
    val password = "abcdefghabcdefgh"
    val encryptResult = AESCrypt.enCrypt(json, password)
    //模拟写入数据库
    val bufferedWriter = BufferedWriter(FileWriter("api.db"))
    bufferedWriter.write(encryptResult)
    bufferedWriter.close()

    //读取加密后的数据
    val bufferedReader = BufferedReader(FileReader("api.db"))
    val cryptApi = bufferedReader.readLine()
    val api = AESCrypt.deCrypt(cryptApi, password)
    println("解密后的数据：$api")
    bufferedReader.close()
}

//----------------------------AES加密与解密-----------------------------
fun testAES() {
    val content = "我还是孙凌霄"
    //密钥长度要求16位
    val password = "1234567812345678"
    val encryptResult = AESCrypt.enCrypt(content, password)
    println("AES加密结果：$encryptResult")

    val decryptResult = AESCrypt.deCrypt(encryptResult, password)
    println("AES解密结果：$decryptResult")

}

//使用方式和DES类似
object AESCrypt {

    //加密
    fun enCrypt(input: String, password: String): String {
        //只用到算法，没有用到工作模式、填充模式
        //也可以传算法/工作模式/填充模式的字符串
        //注意：如果是CBC工作模式 还需要在Cipher初始化额外传入AlgorithmParameters参数
        val cipher = Cipher.getInstance("AES")
        //初始化cipher
        val keySpec = SecretKeySpec(password.toByteArray(), "AES")
        cipher.init(Cipher.ENCRYPT_MODE, keySpec)
        val encryptByteArray = cipher.doFinal(input.toByteArray())
        return String(Base64.getEncoder().encode(encryptByteArray))
    }

    //解密
    fun deCrypt(input: String, password: String): String {
        val cipher = Cipher.getInstance("AES")
        //初始化cipher
        val keySpec = SecretKeySpec(password.toByteArray(), "AES")
        cipher.init(Cipher.DECRYPT_MODE, keySpec)
        //input经过base64编码，所以此处解密需要base64解码再进行AES解密
        val encryptByteArray = cipher.doFinal(Base64.getDecoder().decode(input.toByteArray()))
        return String(encryptByteArray)
    }
}

//----------------------------DES加密与解密-----------------------------
fun testDES() {

    //DES密钥：长度要求8位
    val password = "12345678"
    val content = "我是孙凌霄"
    //加密
    val enCrypt = DESCrypt.enCrypt(content, password)
    println("DES加密结果为：$enCrypt")

    //解密
    val decrypt = DESCrypt.decrypt(enCrypt, password)
    println("DES解密结果为：${String(decrypt)}")

    /** 经过base64编码/解码
     *    DES加密结果为：+TQlaKQLZ9cXOXe7QtFHDw==
     *    DES解密结果为：我是孙凌霄
     */
}

//单例模式的DES加密、解密
object DESCrypt {

    //DES加密
    fun enCrypt(input: String, password: String): String {
        //1.创建Cipher对象(DES表示算法名称)
        val cipher = Cipher.getInstance("DES")
        //2.初始化Cipher对象(加密/解密模式、key)
        //创建key对象：首先创建key工厂对象
        val keyFactory = SecretKeyFactory.getInstance("DES")
        val keySpec = DESKeySpec(password.toByteArray())
        val key: Key? = keyFactory.generateSecret(keySpec)
        cipher.init(Cipher.ENCRYPT_MODE, key)
        //加密 可以返回一个ByteArray
        val encryptByteArray = cipher.doFinal(input.toByteArray(Charsets.UTF_8))
        //为了方便查看可以返回一个String
        //使用了key加密，在加密的时候会导致原字符串长度变长，由于utf-8三个字节对应一个汉字会导致乱码
        //所以通常在做加密时配合使用base64可以解决乱码问题
        return String(Base64.getEncoder().encode(encryptByteArray))
    }

    //DES解密
    fun decrypt(input: String, password: String): ByteArray {
        val cipher = Cipher.getInstance("DES")
        val keyFactory = SecretKeyFactory.getInstance("DES")
        val keySpec = DESKeySpec(password.toByteArray())
        val key: Key = keyFactory.generateSecret(keySpec)
        cipher.init(Cipher.DECRYPT_MODE, key)
        //由于在加密时将返回结果进行base64编码，所以此处需要使用base64解码
        return cipher.doFinal(Base64.getDecoder().decode(input.toByteArray()))
    }
}
